package com.wejoy.video.middle.station.api.controller;


import com.alibaba.fastjson.JSON;
import com.wejoy.video.middle.station.data.exception.AuthException;
import com.wejoy.video.middle.station.data.exception.BusinessException;
import com.wejoy.video.middle.station.data.util.ConstantValues;
import com.wejoy.video.middle.station.data.util.EnvUtil;
import com.wejoy.video.middle.station.data.vo.RespJSON;
import com.wejoy.video.middle.station.data.vo.ReturnCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.QueryTimeoutException;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.server.UnsupportedMediaTypeStatusException;

import javax.servlet.http.HttpServletRequest;
import java.text.ParseException;

/**
 * 统一异常处理
 *
 * @author admin
 */
@ControllerAdvice
public class ExceptionController {
    private final static Logger LOGGER = LoggerFactory.getLogger(ExceptionController.class);
    @Autowired
    private RedisTemplate redisTemplate;

    @ExceptionHandler(value = IllegalArgumentException.class)
    @ResponseBody
    public RespJSON illegalArgumentException(HttpServletRequest req, Exception e) {
        if (EnvUtil.isDev()) {
            LOGGER.error("异常", e);
        } else {
            LOGGER.info("异常: {}", e.getMessage());
        }
        logErrDetail(req);
        return RespJSON.returnCodeWithMessage(ReturnCode.CUSTOM_MESSAGE, "参数错误: " + e.getMessage());
    }

    @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
    @ResponseBody
    public RespJSON methodArgumentTypeMismatchException(HttpServletRequest req, Exception e) {
        logErrDetail(req);
        String field = ((MethodArgumentTypeMismatchException) e).getName();
        String rejectedValue = ((MethodArgumentTypeMismatchException) e).getValue().toString();
        String message = "参数：" + field + "注入的值不合法：" + rejectedValue;
        LOGGER.error("入参错误", e);
        return RespJSON.returnCodeWithMessage(ReturnCode.CUSTOM_MESSAGE, message);
    }

    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public RespJSON customException(HttpServletRequest req, BusinessException e) {
        if (EnvUtil.isDev()) {
            LOGGER.error("自定义异常", e);
        } else {
            LOGGER.info("自定义异常: {}", e.getMessage());
        }
        logErrDetail(req);
        return RespJSON.returnCodeWithMessage(ReturnCode.CUSTOM_MESSAGE, e.getMessage());
    }

    @ExceptionHandler(value = AuthException.class)
    @ResponseBody
    public RespJSON authException(HttpServletRequest req, AuthException e) {
        logErrDetail(req);
        if (e.getReturnCode() != null) {
            return RespJSON.returnCode(e.getReturnCode());
        } else {
            return RespJSON.returnCode(ReturnCode.CODE_OK, e.getMessage());
        }
    }

    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    public RespJSON bindException(HttpServletRequest req, Exception e) {
        logErrDetail(req);
        FieldError fieldError = ((BindException) e).getBindingResult().getFieldError();
        String field = fieldError.getField();
        Object rejectedValue = fieldError.getRejectedValue();
        String message = "参数：" + field + "注入的值不合法：" + rejectedValue;
        LOGGER.error("入参错误", e);
        return RespJSON.returnCodeWithMessage(ReturnCode.CUSTOM_MESSAGE, message);
    }

    @ExceptionHandler(value = ParseException.class)
    @ResponseBody
    public RespJSON parseException(HttpServletRequest req, Exception e) {
        logErrDetail(req);
        return RespJSON.returnCode(ReturnCode.PARSE_ERROR);
    }

    @ExceptionHandler(value = UnsupportedMediaTypeStatusException.class)
    @ResponseBody
    public RespJSON unsupportedMediaTypeStatusException(HttpServletRequest req, Exception e) {
        logErrDetail(req);
        return RespJSON.returnCustomCodeWithMessage(ReturnCode.SYS_ERROR.getCode(), "错误的请求格式");
    }

    @ExceptionHandler(value = ServerWebInputException.class)
    @ResponseBody
    public RespJSON serverWebInputException(HttpServletRequest req, Exception e) {
        logErrDetail(req);
        LOGGER.error(e.getMessage(), e);
        return RespJSON.returnCodeWithMessage(ReturnCode.CUSTOM_MESSAGE, "请在body中传参");
    }

    @ExceptionHandler(value = QueryTimeoutException.class)
    @ResponseBody
    public RespJSON queryTimeoutException(HttpServletRequest req, Exception e) {
        logErrDetail(req);
        LOGGER.error(e.getMessage(), e);
        ((LettuceConnectionFactory)redisTemplate.getConnectionFactory()).initConnection();
        LOGGER.info("reset redis connection");
        return RespJSON.returnCode(ReturnCode.SYS_ERROR);
    }

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public RespJSON defaultErrorHandler(HttpServletRequest req, Exception e) {
        logErrDetail(req);
        LOGGER.error(e.getMessage(), e);
        return RespJSON.returnCode(ReturnCode.SYS_ERROR);
    }


    /**
     * 打印异常参数等信息
     * @param req
     */
    private void logErrDetail(HttpServletRequest req) {
        LOGGER.error("异常请求: {}", req.getRequestURI());
        LOGGER.error("异常参数: {}", JSON.toJSONString(req.getParameterMap()));
        Object requestBody = req.getAttribute(ConstantValues.REQUEST_BODY);
        if (null == requestBody) {
            requestBody = "";
        }
        LOGGER.error("异常参数: {}", requestBody.toString());
        LOGGER.error("异常UA: {}", req.getHeader("user-agent"));
        LOGGER.error("异常referer: {}", req.getHeader("referer"));
        LOGGER.error("异常Content-Type: {}", req.getHeader("Content-Type"));
    }
}